| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151 |
- 'use client';
- import './style.scss';
- import 'animate.css';
- import { use, useEffect, useState } from 'react';
- import { useDonationAlert } from '@/hooks/useDonationAlert';
- import { DonationAlertConfig, DonationAlertData } from '@/types/donation';
- import { fetchApi } from '@/lib/utils/client';
- import View from './view';
- type Props = {
- params: Promise<{ widgetToken: string }>;
- searchParams: Promise<{ [key: string]: string|string[]|undefined }>;
- };
- const DEFAULT_CONFIG: DonationAlertConfig = {
- id: 0,
- title: '',
- amount: 0,
- matchType: 0,
- message: '',
- playDelaySec: 0,
- displayDurationSec: 10,
- popupEffect: null,
- textEffect: null,
- nicknameFontFamily: null,
- nicknameFontSize: 24,
- nicknameFontColor: '#FFD700',
- amountFontFamily: null,
- amountFontSize: 24,
- amountFontColor: '#FF6B35',
- messageFontFamily: null,
- messageFontSize: 18,
- messageFontColor: '#FFFFFF',
- templateFontFamily: null,
- templateFontSize: 24,
- templateFontColor: '#FFFFFF',
- enableImage: false,
- imageUrl: null,
- enableSound: false,
- soundUrl: null,
- isActive: true
- };
- function matchConfig(configs: DonationAlertConfig[], amount: number): DonationAlertConfig
- {
- // 1순위: Exact 매칭 (MatchType === 1)
- const exact = configs.find(c => c.matchType === 1 && c.amount === amount && c.isActive);
- if (exact) {
- return exact;
- }
- // 2순위: MinThreshold (MatchType === 0) — 금액 이상 중 가장 높은 것
- const thresholds = configs
- .filter(c => c.matchType === 0 && c.amount <= amount && c.isActive)
- .sort((a, b) => b.amount - a.amount);
- return thresholds[0] ?? DEFAULT_CONFIG;
- }
- export default function AlertPage({ params, searchParams }: Props)
- {
- const { widgetToken } = use(params);
- const sp = use(searchParams);
- const isPreview = sp.preview === '1';
- const hubUrl = process.env.NEXT_PUBLIC_API_URL + '/hubs/donation';
- const { current, remoteState, onAlertComplete } = useDonationAlert(widgetToken, hubUrl);
- const [configs, setConfigs] = useState<DonationAlertConfig[]>([]);
- const [previewConfig, setPreviewConfig] = useState<DonationAlertConfig|null>(null);
- // API에서 config 목록 로드
- useEffect(() => {
- fetchApi<{ list: DonationAlertConfig[] }>(`/api/widget/alert/config/${widgetToken}`, { silent: true }).then(res => {
- if (res.success && res.data?.list) {
- setConfigs(res.data.list);
- }
- }).catch(() => {});
- }, [widgetToken]);
- // postMessage 수신 (미리보기 모드)
- const [testAlert, setTestAlert] = useState<DonationAlertData|null>(null);
- useEffect(() => {
- if (!isPreview) {
- return;
- }
- const handler = (event: MessageEvent) => {
- if (event.origin !== window.location.origin) {
- return;
- }
- if (event.data?.type === 'ALERT_PREVIEW') {
- setPreviewConfig({
- ...DEFAULT_CONFIG,
- ...event.data.config,
- });
- }
- if (event.data?.type === 'ALERT_TEST') {
- setTestAlert({
- alertID: Date.now(),
- donationID: 0,
- correlationID: '',
- sponsorMemberID: 0,
- sendName: event.data.sendName || '테스트유저',
- amount: event.data.amount || 1000,
- netAmount: event.data.amount || 1000,
- message: event.data.message || null,
- channelID: 0,
- channelName: '',
- crewMemberID: null,
- crewMemberNickname: null,
- isTest: true,
- createdAt: new Date().toISOString()
- });
- }
- };
- window.addEventListener('message', handler);
- return () => window.removeEventListener('message', handler);
- }, [isPreview]);
- // 미리보기 테스트 알림 or 실제 알림
- const activeAlert = isPreview ? testAlert : current;
- const config = activeAlert ? (previewConfig ?? matchConfig(configs, activeAlert.amount)) : null;
- const handleComplete = () => {
- if (isPreview) {
- setTestAlert(null);
- } else {
- onAlertComplete();
- }
- };
- return (
- <div className="alert-page">
- {activeAlert && config && (
- <View
- key={activeAlert.alertID}
- alert={activeAlert}
- config={config}
- isAudioOnly={remoteState.isAudioOnly}
- isVideoOnly={remoteState.isVideoOnly}
- onComplete={handleComplete}
- />
- )}
- </div>
- );
- }
|